// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package os

import (
	"errors"
	"internal/itoa"
)

// fastrand由运行时提供。
// 我们生成随机的临时文件名，这样就有一个很好的
// 文件可能还不存在-将
// TempFile中的尝试次数保持在最低限度。
func fastrand() uint32

func nextRandom() string {
	return itoa.Uitoa(uint(fastrand()))
}

// CreateTemp在目录dir中创建一个新的临时文件，
// 打开该文件进行读写，并返回结果文件。
// 文件名是通过采用模式并在末尾添加随机字符串生成的。
// 如果模式包含一个“*”，则随机字符串将替换最后一个“*”。
// 如果dir是空字符串，则CreateTemp将使用TempDir返回的临时文件的默认目录。
// 同时调用CreateTemp的多个程序或goroutine将不会选择同一个文件。
// 调用者可以使用文件名方法查找文件的路径名。
// 当文件不再需要时，调用者有责任删除该文件。
func CreateTemp(dir, pattern string) (*File, error) {
	if dir == "" {
		dir = TempDir()
	}

	prefix, suffix, err := prefixAndSuffix(pattern)
	if err != nil {
		return nil, &PathError{Op: "createtemp", Path: pattern, Err: err}
	}
	prefix = joinPath(dir, prefix)

	try := 0
	for {
		name := prefix + nextRandom() + suffix
		f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600)
		if IsExist(err) {
			if try++; try < 10000 {
				continue
			}
			return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
		}
		return f, err
	}
}

var errPatternHasSeparator = errors.New("pattern contains path separator")

// 前缀和后缀按最后一个通配符“*”拆分模式，如果适用，
// 返回前缀作为“*”之前的部分，返回后缀作为“*”之后的部分。
func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
	for i := 0; i < len(pattern); i++ {
		if IsPathSeparator(pattern[i]) {
			return "", "", errPatternHasSeparator
		}
	}
	if pos := lastIndex(pattern, '*'); pos != -1 {
		prefix, suffix = pattern[:pos], pattern[pos+1:]
	} else {
		prefix = pattern
	}
	return prefix, suffix, nil
}

// MkdirTemp在目录dir中创建一个新的临时目录
// 并返回新目录的路径名。
// 新目录的名称是通过在模式的末尾添加一个随机字符串生成的。
// 如果模式包含一个“*”，则随机字符串将替换最后一个“*”。
// 如果dir是空字符串，则MkdirTemp将使用TempDir返回的临时文件的默认目录。
// 同时调用MkdirTemp的多个程序或goroutine将不会选择同一目录。
// 当目录不再需要时，调用者有责任删除该目录。
func MkdirTemp(dir, pattern string) (string, error) {
	if dir == "" {
		dir = TempDir()
	}

	prefix, suffix, err := prefixAndSuffix(pattern)
	if err != nil {
		return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err}
	}
	prefix = joinPath(dir, prefix)

	try := 0
	for {
		name := prefix + nextRandom() + suffix
		err := Mkdir(name, 0700)
		if err == nil {
			return name, nil
		}
		if IsExist(err) {
			if try++; try < 10000 {
				continue
			}
			return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
		}
		if IsNotExist(err) {
			if _, err := Stat(dir); IsNotExist(err) {
				return "", err
			}
		}
		return "", err
	}
}

func joinPath(dir, name string) string {
	if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) {
		return dir + name
	}
	return dir + string(PathSeparator) + name
}

// 字符串包中的LastIndexByte。
func lastIndex(s string, sep byte) int {
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] == sep {
			return i
		}
	}
	return -1
}
